package com.atguigu.gmall.index.utils;

import com.google.common.base.Charsets;
import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;

import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.Timer;
import java.util.TimerTask;

//分布式锁工具类
@Component
public class DistributedLockUtil {

    @Autowired
    private StringRedisTemplate redisTemplate;
    //加锁方法
    public Boolean tryLock(String lockName,String uuid,Integer expireTime){
        //lua脚本
        String script = "if redis.call('exists', KEYS[1]) == 0 or redis.call('hexists', KEYS[1], ARGV[1]) == 1 " +
                        "then    redis.call('hincrby', KEYS[1], ARGV[1], 1) " +
                                "redis.call('expire', KEYS[1], ARGV[2]) " +
                        "   return 1 " +
                        "else " +
                        "   return 0 " +
                        "end";
        //获取锁 加上过期时间
        Boolean flag = redisTemplate.execute(new DefaultRedisScript<>(script, Boolean.class), Arrays.asList(lockName), uuid, expireTime.toString());
        if (!flag){
            //如果获取锁失败 则重试
            try {
                Thread.sleep(50);
                this.tryLock(script,uuid,expireTime);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //为防止设置的时间不过 设置过期时间
        this.renewExpire(lockName,uuid,expireTime);
        return true;
    }

    //解锁方法
    public void unLock(String lockName, String uuid){
        String script = "if redis.call('hexists', KEYS[1], ARGV[1]) == 0 " +
                "then " +
                "   return nil " +
                "elseif redis.call('hincrby', KEYS[1], ARGV[1], -1) == 0 " +
                "then " +
                "   return redis.call('del', KEYS[1]) " +
                "else " +
                "   return 0 " +
                "end";
        // 返回结果集类型不要使用Boolean，nil和0都是false，这里可以使用Long
        Long flag = this.redisTemplate.execute(new DefaultRedisScript<>(script, Long.class), Arrays.asList(lockName), uuid);
        if (flag == null){
            throw new RuntimeException("恶意释放锁资源！");
        }
    }

    //设置自动过期
    public void renewExpire(String lockName,String uuid,Integer expireTime){
        String script = "if redis.call('hexists', KEYS[1], ARGV[1]) == 1 " +
                "then " +
                "   return redis.call('expire', KEYS[1], ARGV[2]) " +
                "else " +
                "   return 0 " +
                "end";
        new Timer().schedule(new TimerTask() {
            @Override
            public void run() {
                Boolean flag = redisTemplate.execute(new DefaultRedisScript<>(script, Boolean.class), Arrays.asList(lockName), uuid, expireTime.toString());
                if (flag){
                    //如果这一次续期成功 则可能需要下一次续期
                    renewExpire(lockName,uuid,expireTime);
                }
            }
        },expireTime *1000 /3);

    }
    //重试定时任务
    public static void main(String[] args) {
        //测试布隆过滤器
        //初始化布隆过滤器 长度为20 偏差率0.3
        BloomFilter<CharSequence> bloomFilter = BloomFilter.create(Funnels.stringFunnel(Charsets.UTF_8), 20, 0.3);
        bloomFilter.put("1");
        bloomFilter.put("2");
        bloomFilter.put("3");
        bloomFilter.put("4");
        bloomFilter.put("5");
        bloomFilter.put("6");
        bloomFilter.put("7");
        bloomFilter.put("8");
        bloomFilter.put("9");
        System.out.println(bloomFilter.mightContain("1"));
        System.out.println(bloomFilter.mightContain("3"));
        System.out.println(bloomFilter.mightContain("5"));
        System.out.println(bloomFilter.mightContain("7"));
        System.out.println(bloomFilter.mightContain("9"));
        System.out.println(bloomFilter.mightContain("10"));
        System.out.println(bloomFilter.mightContain("11"));
        System.out.println(bloomFilter.mightContain("12"));
        System.out.println(bloomFilter.mightContain("13"));
        System.out.println(bloomFilter.mightContain("14"));
        System.out.println(bloomFilter.mightContain("15"));
        System.out.println(bloomFilter.mightContain("16"));
        System.out.println(bloomFilter.mightContain("17"));
        System.out.println(bloomFilter.mightContain("18"));
        System.out.println(bloomFilter.mightContain("19"));

        //输出当前时间
//        System.out.println(System.currentTimeMillis());
//        new Timer().schedule(new TimerTask() {
//            @Override
//            public void run() {
//                System.out.println(System.currentTimeMillis());
//            }
//        },5000,10000);
    }
}
